<?php

namespace App\Model;


use App\Services\AlipayService;
use App\Services\Wechat;
use App\Services\WechatService;


use Hyperf\Collection\Collection;
use Hyperf\HttpServer\Contract\RequestInterface;
use Phper666\JWTAuth\Util\JWTUtil;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use DateTimeZone;

/**
 * App\User
 *
 * @property int $id
 * @property string|null $nickname 昵称
 * @property string|null $avatar 头像
 * @property int $balance 账户余额
 * @property int $frozen_balance 冻结余额 不可提现
 * @property string $openid
 * @property string|null $api_token
 * @property string|null $number 编号
 * @property string $source 来源
 * @property-read int|null $ic_card_applies_count
 * @property-read int|null $ic_cards_count
 * @property-read int|null $notifications_count
 * @property-read int|null $orders_count
 * @property-read int|null $payment_orders_count
 * @property-read int|null $coupon_records_count
 */
class User extends Model
{
    public bool $timestamps = true;
    protected ?string $table = 'users';

    const SOURCE_ALI_PAY = 'ali_pay';
    const SOURCE_WE_CHAT = 'we_chat';

    const WECHAT_LOGIN = 1;
    const ALIPAY_LOGIN =2;

    const SOURCE_MAP = [
        self::SOURCE_ALI_PAY => '支付宝',
        self::SOURCE_WE_CHAT => '微信',
    ];

    const SOURCE_TO_PAYMENT_TYPE = [
        self::SOURCE_ALI_PAY => Bill::PAYMENT_TYPE_ALI_PAY,
        self::SOURCE_WE_CHAT => Bill::PAYMENT_TYPE_WE_CHAT_PAY,
    ];


    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected array $fillable = [
        'nickname', 'avatar','login_type','openid','nickname','number','avatar','balance',
        'frozen_balance','phone','vip_time','is_vip','last_login'
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected array $hidden = [
        'api_token',
    ];

    protected array $casts = [
        'balance' => 'decimal:2',
        'frozen_balance' => 'decimal:2',
    ];

    public function wxLogin($params){
        $uri = 'https://api.weixin.qq.com/sns/jscode2session';
        $get['appid'] = config('wxpay.appid');
        $get['secret'] = config('wxpay.appsecret');
        $get['js_code'] = $params['code'];
        $get['grant_type'] = 'authorization_code';
        $result = Wechat::curlGet($uri,$get);
        $result = json_decode($result,true);
        if(!isset($result['openid'])){
            return [];
        }
        $info = $this->where('openid',$result['openid'])
            ->where('login_type',self::WECHAT_LOGIN)
            ->first();
        if(!$info){
            if(!$params['nickname']){
                $save['nickname'] = '用户'.time();
            }
            if(!$params['avatar']){
                $save['avatar'] = '//ylc.yimitongxun.com/public/miniprogram/static/image/profile.png';
            }
            $save['openid'] = $result['openid'];
            $save['login_type'] = self::WECHAT_LOGIN;
            $info = self::create($save);
        }
        return $info;
    }

    public function aliLogin($params){
        $result =  AlipayService::getUserId($params['code']);
        return $result;
    }

    /**构造登陆态内容
     * @param $info
     * @return array
     */
    public function makeInfo($info): array
    {
        $user = [];
        $user['uid'] = $info->id;
        $user['id'] = $info->id;
        $user['nickname'] = $info->nickname;
        $user['phone'] = $info->phone;
        $user['avatar'] = $info->avatar;
        $user['number'] = $info->number;
        $user['openid'] = $info->openid;
        $user['login_type'] = $info->login_type;
        $user['is_vip'] = $info->is_vip;
        $user['vip_time'] = $info->vip_time;
        return $user;
    }

    /**获取登陆态
     * @param RequestInterface $request
     * @return array
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface|RedisException
     */
    public function getInfo(RequestInterface $request,): array
    {
        $token = $request->getHeaderLine('Authorization');
        if ($token) {
//            $tokenArr = explode("Bearer ", $token);
//            $bool= redis()->exists($tokenArr[1]);
//            if(!$bool){
//                return [];
//            }
            $jwtArray = JWTUtil::getUserData($token);
            if (!isset($jwtArray['id'])) {
                return [];
            }
            $jwtArray['now'] = time();
            if ($jwtArray) {
                $jwtArray['exp'] = $jwtArray['exp']->setTimezone(new DateTimeZone('Asia/Shanghai'));
                $jwtArray['exp_time'] = $jwtArray['exp']->getTimestamp();
                if (time() > $jwtArray['exp_time']) {
                    return [];
                }
                if ($jwtArray['vip_time']) {
                    if (strtotime($jwtArray['vip_time']) < time()) {
                        self::where('id',$jwtArray['id'])->update(['is_vip' => 0]);
                        $jwtArray['is_vip'] = 0;
                    }else{
                        self::where('id',$jwtArray['id'])->update(['is_vip' => 1]);
                        $jwtArray['is_vip'] = 1;
                    }
                }
            }
            return $jwtArray;
        }
        return [];
    }

    /**添加常用设备
     * @param $userId
     * @param $deviceId
     * @return void
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function addUseCount($userId,$deviceId){
        $key = 'user_device_cache'.$userId;
        if(redis()->exists($key)){
            redis()->zIncrBy($key, 1, $deviceId);
        }else{
            redis()->zAdd($key,1,$deviceId);
        }
    }

    /**常用设备列表
     * @param $userId
     * @return void
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function getUseCount($userId){
        $key = 'user_device_cache'.$userId;

        $list = redis()->zRevRange($key, 0, -1,true);
        return $list;
    }


    public function paymentOrders()
    {
        return $this->hasMany(PaymentOrder::class);
    }

    public function icCardApplies()
    {
        return $this->hasMany(IcCardApply::class);
    }

    public function getUsingOrder($sn, $channelNumber)
    {
        return $this->orders()->inUsing()->where('sn', $sn)->where('channel_number', $channelNumber)->first();
    }


    public function getPaymentTypeOfSource()
    {
        return Arr::get(self::SOURCE_TO_PAYMENT_TYPE, $this->source);
    }


    public function getSourceDes()
    {
        return Arr::get(self::SOURCE_MAP, $this->source);
    }

    public function isSubscribe()
    {
        $subscribe = true;

        $app = WechatService::getApp();
        if($this->source == User::SOURCE_WE_CHAT) {
            $userInfo = $app->user->get($this->openid);

            $subscribe = (boolean) Arr::get($userInfo, 'subscribe');

            logger('user info', $userInfo);
        }

        return $subscribe;
    }


    /**
     * @param $fee
     * 扣减余额
     */
    public function decrementBalance($fee)
    {
        $frozenBalanceUseFee =  min($this->frozen_balance, $fee);
        $balanceUseFee =  $fee - $frozenBalanceUseFee;

        $frozenBalanceUseFee > 0 && $this->decrement('frozen_balance', $frozenBalanceUseFee);
        $balanceUseFee > 0 && $this->decrement('balance', $balanceUseFee);
    }

    /**退款安心充 后业务
     * @param $serviceId
     * @return void
     */
    public function refundStatus($serviceId){
        $service = Service::where('id',$serviceId)->first(['days','fee']);
        $this->balance+=$service->fee;
        if($this->vip_time){
            $newDate = date('Y-m-d', strtotime("-{$service->days} days", strtotime($this->vip_time)));
            if(strtotime($newDate)<=time()){
                $this->is_vip = 0;
            }
            $this->vip_time = $newDate;
        }
        $this->save();
    }

    /**
     * @param array $withCount
     */
    public function setWithCount(array $withCount): void
    {
        $this->withCount = $withCount;
    }

    /**升级为vip
     * @param $serviceId
     * @return void
     */
    public function upVip($serviceId){
        $serviceModel = Service::where('id',$serviceId)->first();
        if($serviceModel){
            $this->is_vip = 1;
            if(!$this->vip_time){
                $now = date('Y-m-d',time());
                $this->vip_time = date('Y-m-d', strtotime("+{$serviceModel->days} days", strtotime($now))); // 获取当前日期加n天后的日期
            }else{
                if(strtotime($this->vip_time) > time()){
                    $this->vip_time = date('Y-m-d', strtotime("+{$serviceModel->days} days", strtotime($this->vip_time)));
                }else{
                    $now = date('Y-m-d',time());
                    $this->vip_time = date('Y-m-d', strtotime("+{$serviceModel->days} days", strtotime($now)));
                }
            }
            $this->save();
        }
    }

    public function getTotalBalance()
    {
        return round($this->balance + $this->frozen_balance, 2);
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany|Order
     */
    public function orders()
    {
        return $this->hasMany(Order::class,'id','user_id');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany|Order
     */
    public function couponRecords()
    {
        return $this->hasMany(CouponRecord::class);
    }


    /**
     * @param Device|Model $device
     * @return mixed
     */
    public function getCanUseCouponRecord(Device $device)
    {
        $couponIds = $device->coupons->pluck('id')->toArray();

        return $this->couponRecords()->whereIn('coupon_id', $couponIds)->canUse()->oldest()->first();
    }

//    public function refund($fee)
//    {
//        //一年内的 有可退款金额的订单 取50个 依次退款
//        $paymentOrders = $this
//            ->paymentOrders()
//            ->whereNotNull('paid_at')
//            //支付方式
//            ->whereIn('platform', [Bill::PAYMENT_TYPE_WE_CHAT_PAY, Bill::PAYMENT_TYPE_ALI_PAY])
//            ->whereIn('type', [Bill::TYPE_CHARGE_BALANCE])
//            ->where('created_at', '>', now()->subYear()->toDateTimeString())
//            ->select(["*", \DB::raw('abs(fee) - refunded_fee as can_refunded_fee')])
//            ->having('can_refunded_fee', '>', 0)
//            ->orderByDesc("can_refunded_fee")
//            ->oldest()
//            ->take(50)
//            ->get();
//
//        if($paymentOrders->sum('can_refunded_fee') < $fee) {
//            abort(400, '可退款账单金额不足, 请检查提现是否异常');
//        }
//
//        $refundedFee = 0;
//
//        foreach ($paymentOrders as $paymentOrder) {
//
//            //只要还有金额 一直退到退完为止
//            if($fee > $refundedFee) {
//                //从可退款金额和还需要退款的金额中取最小值
//                $onceFee = min($paymentOrder->can_refunded_fee, $fee - $refundedFee);
//
//                try {
//                    $paymentOrder->outPaymentRefund($onceFee, '账户提现');
//
//                } catch (\Exception $exception) {
//                    logger('退款失败 ' . $exception->getMessage());
//                    break;
//                }
//
//                $refundedFee += $onceFee;
//            } else {
//                break;
//            }
//        }
//
//        if($refundedFee < $fee) {
//            logger('退款未完成 $refundedFee ' . $refundedFee);
//        }
//    }

    public function toAdminString()
    {
        return '[' . $this->number . '] ' . $this->nickname;

    }


    public function generateNumber()
    {
        $this->number = self::getNumber();

        while (User::where('number', $this->number)->exists()) {
            $this->number = self::getNumber();
        }

        $this->save();
    }

    public static function getNumber()
    {
        return chr(rand(65, 90)) . chr(rand(65, 90)) . chr(rand(65, 90)) . str_pad(rand(0, 9999), 4, '0', STR_PAD_LEFT);
    }


    /**
     * @param Request $request
     * @return User
     */
    public static function current(Request $request)
    {
        return $request->user();
    }


}
